using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Diagnostics;
using System.Xml;
using System.Collections.Generic;

namespace Dida.Dxml
{
	internal static class CodeGenToolkit
	{
		public static void EmitLoadValue(ILGenerator ilGen, DidaXml.Parser parser)
		{
			switch (parser.parserType)
			{
				case DidaXml.ParserType.ComplexParser:
					break;

				case DidaXml.ParserType.ExternalFile:
					break;

				case DidaXml.ParserType.Value:
					break;
			}
		}

		public static MethodBuilder EmitBinarySearch(SortedList<string, int> list,Type parentType)
		{
			MethodBuilder ret = SerializationAssembly.DefineGlobalMethod(
				typeof(int), new Type[] { typeof(string) });
			ILGenerator ilGen = ret.GetILGenerator();
			Label errorLabel = ilGen.DefineLabel();
			EmitBinarySearchRecursive(ilGen, list, 0, list.Count,errorLabel);

			// emit the error handling code
			ilGen.MarkLabel(errorLabel);

			MethodInfo concat3Method = typeof(string).GetMethod("Concat",
				new Type[] { typeof(string), typeof(string), typeof(string) });

			ilGen.Emit(OpCodes.Ldstr, "Invalid value for " + parentType.Name + ": \"");
			ilGen.Emit(OpCodes.Ldarg_0);
			ilGen.Emit(OpCodes.Ldstr, "\".");
			ilGen.Emit(OpCodes.Call, concat3Method);

			ilGen.Emit(OpCodes.Newobj, typeof(Exception).GetConstructor(
				new Type[] { typeof(string) }));
			ilGen.Emit(OpCodes.Throw);

			return ret;
		}

		private static void EmitBinarySearchRecursive(ILGenerator ilGen,
			SortedList<string, int> list, int min, int max,Label errorLabel)
		{
			MethodInfo compareMethod = typeof(string).GetMethod(
				"Compare", new Type[] { typeof(string),typeof(string)});

			if (max - min == 1)
			{
				ilGen.Emit(OpCodes.Ldstr, list.Keys[min]);
				ilGen.Emit(OpCodes.Ldarg_0);
				ilGen.Emit(OpCodes.Call, compareMethod);
				ilGen.Emit(OpCodes.Ldc_I4_0);
				ilGen.Emit(OpCodes.Bne_Un, errorLabel);
				ilGen.Emit(OpCodes.Ldc_I4, list.Values[min]);
				ilGen.Emit(OpCodes.Ret);
			}
			else
			{
				int mid = (min + max) / 2;

				Label highPartLabel = ilGen.DefineLabel();
				ilGen.Emit(OpCodes.Ldarg_0);
				ilGen.Emit(OpCodes.Ldstr, list.Keys[min]);
				ilGen.Emit(OpCodes.Call, compareMethod);
				ilGen.Emit(OpCodes.Ldc_I4_M1);
				ilGen.Emit(OpCodes.Bne_Un, highPartLabel);
				EmitBinarySearchRecursive(ilGen, list, min, mid, errorLabel);
				ilGen.MarkLabel(highPartLabel);
				EmitBinarySearchRecursive(ilGen, list, mid, max, errorLabel);
			}
		}
	}

	public static class HelperMethods
	{
		public static string GetAttribute(XmlReader reader, string attrName)
		{
			string ret = reader.GetAttribute(attrName);
			if (ret == null)
			{
				throw new Exception("No \"" + attrName + "\" attribute specified.");
			}

			return ret;
		}

		public static void ThrowParseAttributeException(Exception original,
			XmlReader reader, string name)
		{
			throw new XmlAttributeException(original.Message, reader, name);
		}
	}
}